package 泛型;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/*
【前言】
jdk5.0后java提供了泛型，用大写字母表示，一般用T，表示是一个不确定的具体的类型。
？表示是一群不确定的具体的类型，但？是它们的父类。

正文：？和T的区别
T：代表一种类型。
？：通配符，泛指所有的类型，是所有类型的父类。

用法：
一般而言，T主要用于泛型类的定义、泛型方法的定义，还有具体的变量的类型定义上，如：
--定义泛型类：class Super<T>{}

--定义泛型方法：public <T> void testMethod(T[] a, Collection<T> c)

--修饰泛型类的属性成员：class Sub<T>{T temp;}

以上三种情况，在实际使用中，都要求是指明了具体的泛型类型，才可以使用。而"?"是通配符，代表的是不确定的类，是一群类的父类，所以以上三种情况都不可以用"?"代替T。

那么，通配符，一般是用于定义一个引用变量，以便实现“多态”调用(非真正意义上的多态)。
比如定义一个sup的引用变量，不指定具体的泛型类型，而用通配符表示。如下：

Super<?> sup = null;
sup = new Super<People>();
sup = new Super<Animal>();
......

也可以做方法的形参，定义引用变量，同样是为了实现“多态”调用：
*/
public class T与问号的异同 {
	public static void main(String[] args) {
		//public interface List<E> extends Collection<E> { //接口定义
		List<String> l1 = new ArrayList<String>();
		List<Integer> l2 = new ArrayList<Integer>();
		List<X> l3 = new ArrayList<X>();
		
		l1.add("AAA");
		l2.add(15);
		l3.add(new X(4));
		read(l1);
		read(l2);
		read(l3);
	}

//	public static <T> void read(List<T> list) {
//		for (T o : list) {
//			System.out.println(o);
//		}
//	}
	public static void read(List<?> list) {
		for (Object o : list) {
			System.out.println(o);
		}
	}
}
class X{
	int n;
	public X(int n) {
		this.n=n;
	}
	public String toString() {
		return "["+n+"]";
	}
}
/*
由于泛型在继承上的体现，我们知道，如果B是A的一个子类型（子类或者子接口），而G是具有泛型声明的类或接口，G<B>并不是G<A>的子类型！所以，无法通过多态的方式声明
一个引用变量指向多个子类的实例。G(Number)与G(Integer)就没有父子类关系。
如下：

Super<String> sup1 = new Super<String>();
Super<People> sup2 = new Super<People>();
Super<Animal> sup3 = new Super<Animal>();

同一个类，如果泛型的具体类型不同，定义引用变量在不使用通配符的情况下，需要为每一个具体的实例创建对应的引用变量。
为了弥补这一缺点，通配符出现了，如果利用通配符，那么我们就可以如下声明：

Super<?> sup = null;
sup = new Super<People>();
sup = new Super<Animal>();

从这也可以看出，通配符是没有指代具体的类型的，是多个类型的父类。

补充：

//以下是错误用法：
//编译错误：通配符不能用在创建对象上，右边属于创建集合对象
ArrayList<?> list2 = new ArrayList<?>();

有限制通配符：
? extends A:
G<? extends A> 可以作为G<A>和G<B>的父类，其中B是A的子类

? super A:
G<? super A> 可以作为G<A>和G<B>的父类？ 其中B是A的父类   可以作为G<A>和G<B>的子类？

T extends A 和 T super A：效果同上，唯一的差别还是在？和 T上(即上文所比较的内容)。

无限制通配符和有限制通配符的数据读写问题 
•无限制通配符(?)读写：
读：可以读，尽管被无限制通配符修饰，但不管如何，容器里的元素永远是一个对象，也就是一个Object，所以可以用Object o这个引用变量来读取容器元素，即利用对象多态性。

写：不可以往容器里写入元素，因为写入的元素与容器的具体类型的关系不明确。比如：

ArrayList<?> list = new ArrayList<String>();
list = new ArrayList<Integer>();

list可以指向不同的具体容器，相对应的能接收的元素类型也跟着改变。因此往容器里添加数据时，会因为无法确定所能添加的具体元素类型为何，导致的类型不安全而编译不通过。

注意：可以添加null，因为null是所有类型的成员。

•有限制通配符读写：
① ? extends A：
读：往容器中添加的元素类型，一定是A或A的子类(如果是接口，则为实现类)，因此读取出来的数据，一定可以用A类型来做引用(当然Object也可以)。

写：由于容器的具体类型未知，如果往容器添加元素，无法确保添加进去的具体数据是该容器具体类型的子类还是父类，因此存在类型不安全问题，所以是不允许往里添加数据的。

举个栗子：
例如：有三个类，Creature，Person，Student，其中，Student extends Person，Person extends Creature。

那么，现在声明：List<? extends Creature> list；

如果，此时实例化：list = new ArrayList<Student>()；

再往其中添加数据时，根据list的声明，是可以往其中添加Creature及其子类的实例化对象的，但由于list指向的具体容器为Student类型，根据对象多态性，子类引用不可以指向父
类对象，因此实际容器是无法承装Creature和Person类的实例对象的，从而会引起类型不安全的矛盾。因此，是不允许往容器里写入数据的。

注意：null仍然可以。

②？ super A：
读：不管容器里添加的元素是A还是A的父类的实例对象，它们始终都是Object对象，因此可以读取，用Object类型来做引用(不能用A类型来做引用)。

写：只能写入A类型及其子类的实例对象。因为具体容器的类型最低等级是到A，所以不管容器具体类型为何，它都能保证＞= A，所以A的实例化对象可以被写入(对象多态性)，当然，既
然作为父类的A可以被写入，那么A的子类自然而然也可以被写入容器里了。
*/